<?php
/**
 * JSONRPC class definition
 *   
 * @author Sean Crystal <seancrystal@gmail.com>
 * @package MutantVole
 */

/**
 * Provides some utility methods for JSON-RPC
 * @package MutantVole
 * @subpackage JSONRPC
 */ 
class JSONRPC {
	/**
	 * Make a JSON-RPC request and return the result
	 * @param string $url
	 * @param string $method
	 * @param array $params
	 * @return mixed
	 */
	public static function Request($url, $method, $params = null, $ignore_id = false) {
	    Contract::Precondition(!empty($url), '$url cannot be empty');
	    Contract::Precondition(!empty($method), '$method cannot be empty');
	    
		list($id, $req) = JSONRPC::GenerateRequest($method, $params);
		
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_POST, true);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-type: application/json'));

		$response = curl_exec($ch);	
		curl_close($ch);
		
		$obj = json_decode($response);
		
		if ($ignore_id || $id == $obj->id) {
			return $obj;
		} 

		return false;
	}
	
	/**
	 * Generate a json-encoded RPC request
	 * @param string $method
	 * @param array $params
	 * @return array
	 */
	public function GenerateRequest($method, $params) {
		$id = md5(uniqid(rand(), true));
		
		return array($id, json_encode(array('jsonrpc' => '2.0', 'id' => $id, 'method' => $method, 'params' => $params)));
	}
	
	/**
	 * Generate the structure for a JSON-RPC result
	 * @param string $id
	 * @param mixed $response
	 * @return array
	 */
	public function GenerateResult($id, $response) {
		$result['jsonrpc'] = '2.0';
		$result['id']      = $id;
		$result['result']  = $response;	
		
		return $result;
	}
	
	/**
	 * Generate the structure for a a JSON-RPC error
	 * @param string $id
	 * @param string $message
	 * @param mixed $data
	 * @param integer $code
	 * @return array
	 */
	public function GenerateError($id, $message, $data = null, $code = -32700) {
		$error['jsonrpc'] 		   = '2.0';
		$error['id']      		   = $id;
		$error['error']['code']    = $code;
		$error['error']['message'] = $message;
		$error['error']['data']    = $data;
		
		return $error;
	}
	
	/**
	 * Generate an SMD response for a JSONRPCPresenter object
	 * @param JSONRPCPresenter $presenter
	 * @param string $url
	 * @return string
	 */
	public function GenerateSMD(JSONRPCPresenter $presenter, $url) {
		$smd = new stdClass;
	
		$smd->transport   = "RAW_POST";
		$smd->envelope    = "JSON-RPC";
		$smd->serviceType = "JSON-RPC";
		$smd->serviceUrl  = $url;
		$smd->methods     = array();
		
		$ref = new ReflectionClass(get_class($presenter));
		$methods = $ref->getMethods();
		
		if (is_array($methods)) {
			foreach ($methods as $method) {
				$matches = array();
				
				if ($method->isPublic() && preg_match('/^JSONHandle(.+)$/', $method->getName(), $matches)) {
					$desc             = new stdClass;
					$desc->name       = $matches[1];
					$desc->parameters = array();
					$smd->methods[]   = $desc;
					
					$params = $method->getParameters();
					
					if (is_array($params)) {
						foreach ($params as $param) {
							if ($param->getName() != 'id') {
								$name = $param->getName();
								$p = new stdClass;
								$p->name = $name;
								$desc->parameters[] = $p;
							}
						}
					}
				}
			}
		}
		
		return json_encode($smd);
	}
}

?>
